package org.mar.netty_demo.handler.http;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.*;
import io.netty.handler.stream.ChunkedFile;
import io.netty.handler.stream.ChunkedWriteHandler;

import java.beans.EventHandler;
import java.io.File;
import java.io.RandomAccessFile;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.Objects;
//import javax.activation.MimetypesFileTypeMap;

import static io.netty.handler.codec.http.HttpResponseStatus.*;

public class FileServer {
    public static void main(String[] args) throws InterruptedException {
        new FileServer().run(8080);
    }

    public void run(int port) throws InterruptedException {
        EventLoopGroup parent = new NioEventLoopGroup();
        EventLoopGroup children = new NioEventLoopGroup();
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap().group(parent, children).channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast("http-req-decoder", new HttpRequestDecoder());
                            ch.pipeline().addLast("http-aggregator", new HttpObjectAggregator(65536));
                            ch.pipeline().addLast("http-res-encoder", new HttpResponseEncoder());
                            ch.pipeline().addLast("http-chunked", new ChunkedWriteHandler());
                            ch.pipeline().addLast("file-handler", new FileServerHandler());
                        }
                    });

            ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
            channelFuture.channel().closeFuture().sync();
        } finally {
            parent.shutdownGracefully();
            children.shutdownGracefully();
        }
    }

    private static class FileServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception {
            if (!msg.decoderResult().isSuccess()) {
                sendError(ctx, BAD_REQUEST);
                return;
            }

            if (msg.method() != HttpMethod.GET) {
                sendError(ctx, METHOD_NOT_ALLOWED);
                return;
            }

            String uri = urlFormat(msg.uri());
            File file = new File(uri);
            if (!file.exists()) {
                sendError(ctx, NOT_FOUND);
                return;
            }

            RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
            long fileLength = randomAccessFile.length();
            HttpResponse httpResponse = new DefaultHttpResponse(HttpVersion.HTTP_1_1, OK);
            httpResponse.headers().set(HttpHeaderNames.CONTENT_LENGTH, fileLength);

//            MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap();
            httpResponse.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN);
            if (keepAlive(msg)) {
                httpResponse.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
            }
            ctx.write(httpResponse);
            ctx.write(new ChunkedFile(randomAccessFile, 0, fileLength, 8192));
            ctx.flush();
        }

        private void sendError(ChannelHandlerContext ctx, HttpResponseStatus errorStatus) {
            HttpResponse httpResponse = new DefaultHttpResponse(HttpVersion.HTTP_1_1, errorStatus);
            ctx.writeAndFlush(httpResponse);
        }

        private String urlFormat(String uri) {
            uri = URLDecoder.decode(uri, StandardCharsets.UTF_8).replace("/", File.separator);
            return System.getProperty("user.dir") + File.separator + uri;
        }

        private boolean keepAlive(FullHttpRequest msg) {
            return Objects.equals(msg.headers().get(HttpHeaderNames.CONNECTION), HttpHeaderValues.KEEP_ALIVE.toString());
        }

    }
}
